using System;
using System.Collections.Generic;
using System.Text;
using Lidgren.Library.Network;
using Microsoft.Xna.Framework;
using Lidgren.Library.Network.Xna;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Game1
{
    public class Server : Network
    {
        private NetAppConfiguration nac;
        public NetServer netServer;
        private NetLog Log;
        private List<int> idList; 
        int msgtype; //primeiro atributo da mensagem que indica como a mensagem deve ser decodificada
        
        public Server(string name)
        {
            nac = new NetAppConfiguration("Game", Config.Singleton.ServerPort);
            nac.MaximumConnections = Config.Singleton.MaxClientsperServer;
            nac.ServerName = name;
            Log = new NetLog();
            netServer = new NetServer(nac, Log);
            netServer.StatusChanged += new 
                EventHandler<NetStatusEventArgs>(server_StatusChanged);
            idList = new List<int>();
            idList.Add(0); //adicionando o id padrao do servidor que eh 0
        }
        
        public Server(String name, int port)
        {
            nac = new NetAppConfiguration("Game", Config.Singleton.ServerPort);
            nac.ServerName =name;
            nac.MaximumConnections = Config.Singleton.MaxClientsperServer;
            Log = new NetLog();
            netServer = new NetServer(nac,Log);
            netServer.StatusChanged += new EventHandler<NetStatusEventArgs>(server_StatusChanged);
        }

        void server_StatusChanged(object sender, NetStatusEventArgs e)
        {
            if (e.Connection.Status == NetConnectionStatus.Connected)
            {

                NetMessage msgSent = new NetMessage();
                
                //indica o tipo da mensagem
                msgSent.Write(1);

                //gera um id unico para o player
                msgSent.Write(generateNewId());

                //indica o numero de players presentes
                msgSent.Write(Entitities.EnemyList.Count+1);

                //mandando as posicoes correntes dos outros players
                foreach(Enemy x in Entitities.EnemyList)
                {
                    msgSent.Write(x.id);
                    XnaSerialization.Write(msgSent, x.model.PhysicsBody.Position);
                    XnaSerialization.Write(msgSent, x.model.PhysicsBody.Velocity);
                    XnaSerialization.Write(msgSent, x.model.PhysicsBody.AngularVelocity);
                    XnaSerialization.WriteMatrix(msgSent, x.model.PhysicsBody.Orientation);
                }

                msgSent.Write(Entitities.player.id);                    
                XnaSerialization.Write(msgSent, Entitities.player.model.PhysicsBody.Position);
                XnaSerialization.Write(msgSent, Entitities.player.model.PhysicsBody.Velocity);
                XnaSerialization.Write(msgSent, Entitities.player.model.PhysicsBody.AngularVelocity);
                XnaSerialization.WriteMatrix(msgSent, Entitities.player.model.PhysicsBody.Orientation);  
                netServer.SendMessage(msgSent, e.Connection, NetChannel.Unreliable);

                netServer.Heartbeat();
                HandleMSG();

            }
            if (e.Connection.Status == NetConnectionStatus.Disconnected)
            {
              /*
                msg.Write(2);
                netServer.SendMessage(msg, e.Connection, NetChannel.Unreliable);
           */ }
        }

        public override void Update()
        {
         
            //preparando mensagens para serem mandadas
            NetMessage msgSent = new NetMessage();
            msgSent.Write(0);
            foreach(PhysicObject x in Entitities.PhysicObjectList)
            {
                XnaSerialization.Write(msgSent, x.PhysicsBody.Position);
                XnaSerialization.Write(msgSent, x.PhysicsBody.Velocity);
                XnaSerialization.Write(msgSent, x.PhysicsBody.AngularVelocity);
                XnaSerialization.WriteMatrix(msgSent, x.PhysicsBody.Orientation);
            }

            foreach (Enemy x in Entitities.EnemyList)
            {
                msgSent.Write(x.id);
                XnaSerialization.Write(msgSent, x.model.PhysicsBody.Position);
                XnaSerialization.Write(msgSent, x.model.PhysicsBody.Velocity);
                XnaSerialization.Write(msgSent, x.model.PhysicsBody.AngularVelocity);
                XnaSerialization.WriteMatrix(msgSent, x.model.PhysicsBody.Orientation);
            }



            msgSent.Write(Entitities.player.id);                    
            XnaSerialization.Write(msgSent, Entitities.player.model.PhysicsBody.Position);
            XnaSerialization.Write(msgSent, Entitities.player.model.PhysicsBody.Velocity);
            XnaSerialization.Write(msgSent, Entitities.player.model.PhysicsBody.AngularVelocity);
            XnaSerialization.WriteMatrix(msgSent, Entitities.player.model.PhysicsBody.Orientation); 
 
            netServer.Broadcast(msgSent, NetChannel.Unreliable);
            //enviando as mensagens
            netServer.Heartbeat();
           
            //atitudes do servidor quando este recebe uma mensagem
            HandleMSG();     
        }

        private void HandleMSG()
        {
            NetMessage msgReceived;
            while((msgReceived = netServer.ReadMessage())!=null)
            {
                msgReceived.ResetReadPointer();
                msgtype = msgReceived.ReadInt();
                switch(msgtype)
                {
                    case 0:
                        {
                            int id = msgReceived.ReadInt();
                            foreach (Enemy x in Entitities.EnemyList)
                            {
                                if (x.id == id)
                                {
                                    x.model.PhysicsBody.Position = XnaSerialization.ReadVector3(msgReceived);
                                    x.model.PhysicsBody.Velocity = XnaSerialization.ReadVector3(msgReceived);
                                    x.model.PhysicsBody.AngularVelocity = XnaSerialization.ReadVector3(msgReceived);
                                    x.model.PhysicsBody.Orientation = XnaSerialization.ReadMatrix(msgReceived);
                                }
                            }
                            break;
                        }
                    case 1:
                        {//recebendo os dados do novo usuario conectado;
                            int id = idList[idList.Count-1];
                            Vector3 position = XnaSerialization.ReadVector3(msgReceived);
                            Vector3 velocity = XnaSerialization.ReadVector3(msgReceived);
                            Vector3 angularVelocity = XnaSerialization.ReadVector3(msgReceived);
                            Matrix orientation = XnaSerialization.ReadMatrix(msgReceived);

                            PhysicObject nave = new BoxObject(Base.Content.Load<Model>("Models\\p1_wedge"),
                            new Vector3(2, 1, 2), orientation, position, Entitities.player.model.Camera);
                            nave.Scale = new Vector3(0.001f, 0.001f, 0.001f);

                            Base.Singleton.Components.Add(nave);
                            Enemy enemy = new Enemy("joao da silva", nave);
                            enemy.id = id;
                            Entitities.EnemyList.Add(enemy);

                            //temos entao que mandar os dados desse novo usuario aos players que ja existiam
                            NetMessage newUserMsg = new NetMessage();
                            //tipo da mensagem para que se possa identificar como decodificar
                            newUserMsg.Write(2);
                            //os dados do novo usuario a ser adicionado
                            newUserMsg.Write(id);
                            XnaSerialization.Write(newUserMsg, position);
                            XnaSerialization.Write(newUserMsg, velocity);
                            XnaSerialization.Write(newUserMsg, angularVelocity);
                            XnaSerialization.WriteMatrix(newUserMsg, orientation);
                            netServer.Broadcast(newUserMsg, NetChannel.Unreliable);
                            netServer.Heartbeat();
                            
                            break;
                        }
                    
                }
            }            
        }

        private int generateNewId()
        {
            bool test=false;
            int r;
            do
            {
                r = (new Random()).Next();  
                foreach (int x in idList)
                    if (r == x)
                    {
                        test = true;
                        break;
                    }                
            } while (test == true);
            idList.Add(r);
            return r;
        }
    }
}
